package com.darkpegasus.trillian.ui;

import com.darkpegasus.trillian.parser.AbstractTrillianEvent;
import com.darkpegasus.trillian.parser.Message;
import com.darkpegasus.trillian.parser.SessionUpdate;
import com.darkpegasus.trillian.parser.StatusUpdate;
import com.darkpegasus.util.StringUtil;
import com.google.common.collect.Maps;

import javax.swing.text.*;
import java.awt.*;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * @author alek
 */
public class LogDocumentBuilder {
    private static final Color trillianMessageBlue = new Color(0, 128, 255);
    private static final Color trillianMessageRed = new Color(255, 50, 50);

    private final StyleContext context = new StyleContext();
    private final StyledDocument document = new DefaultStyledDocument(context);

    private final Style outgoingStyle;
    private final Style incomingStyle;
    private final Style messageStyle;
    private final Style notificationStyle;

    private final SimpleDateFormat longFormat;
    private final SimpleDateFormat shortFormat;

    public LogDocumentBuilder() {
        incomingStyle = context.addStyle(null, context.getStyle(StyleContext.DEFAULT_STYLE));
        StyleConstants.setSpaceAbove(incomingStyle, 100);
        StyleConstants.setForeground(incomingStyle, trillianMessageBlue);

        outgoingStyle = context.addStyle(null, context.getStyle(StyleContext.DEFAULT_STYLE));
        StyleConstants.setSpaceAbove(outgoingStyle, 100);
        StyleConstants.setForeground(outgoingStyle, trillianMessageRed);

        messageStyle = context.addStyle(null, context.getStyle(StyleContext.DEFAULT_STYLE));

        notificationStyle = context.addStyle(null, context.getStyle(StyleContext.DEFAULT_STYLE));
        StyleConstants.setForeground(notificationStyle, Color.GRAY);

        longFormat = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss");
        shortFormat = new SimpleDateFormat("HH:mm");
    }

    public void addLogEvent(AbstractTrillianEvent message){
        if(eventBuilderMap.containsKey(message.getClass())){
            eventBuilderMap.get(message.getClass()).addEvent(message);
        }
    }

    private void addText(String text, Style style) {
        try {
            document.insertString(document.getLength(), text, style);
        } catch (BadLocationException e) {
            // Shouldn't happen since we're using the doc length for this
            e.printStackTrace();
        }
    }

    private static interface EventBuilder<T extends AbstractTrillianEvent>{
        public void addEvent(T event);
    }

    private Map<? extends Class, ? extends EventBuilder> eventBuilderMap = Maps.immutableMap(
            Message.class, new EventBuilder<Message>() {
                                public void addEvent(Message event) {
                                    Style senderStyle = (event.getDirection() == Message.Direction.Incoming) ?
                                            incomingStyle : outgoingStyle;

                                    if(!StringUtil.isEmptyOrNull(event.getSenderDisplayName())){
                                        addText("[" + shortFormat.format(event.getTimestamp()) + "] " +
                                                event.getSenderDisplayName() + ": ", senderStyle);
                                        addText(event.getMessageText() + "\n", messageStyle);
                                    } else {
                                        addText("[" + shortFormat.format(event.getTimestamp()) + "] " +
                                                event.getMessageText() + "\n", notificationStyle);
                                    }
                                }
                            },
            SessionUpdate.class, new EventBuilder<SessionUpdate>() {
                                public void addEvent(SessionUpdate event) {
                                    SessionUpdate.UpdateType sessionType = event.getUpdateType();

                                    switch(sessionType){
                                        case Start:
                                            addText("Conversation started at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                        case Stop:
                                            addText("Conversation ended at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                    }
                                }
                            },
            StatusUpdate.class, new EventBuilder<StatusUpdate>() {
                                public void addEvent(StatusUpdate event) {
                                    StatusUpdate.Status userStatus = event.getStatus();
                                    String senderId = event.getSenderId();

                                    switch(userStatus){
                                        case Idle:
                                            addText(senderId + " became idle at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                        case Offline:
                                            addText(senderId + " went offline at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                        case Online:
                                            addText(senderId + " came online at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                        case Away:
                                            addText(senderId + " went away at " + longFormat.format(event.getTimestamp()) + "\n", notificationStyle);
                                            break;
                                    }
                                }
                            }
    );

    public StyledDocument getDocument() {
        return document;
    }
}
